-
-
Notifications
You must be signed in to change notification settings - Fork 368
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Registration API #6246
base: dev/feature
Are you sure you want to change the base?
New Registration API #6246
Conversation
Co-authored-by: Patrick Miller <[email protected]>
Co-authored-by: Patrick Miller <[email protected]>
This can be obtained from the addon instance
public static Collection<SkriptAddon> getAddons() { | ||
return Collections.unmodifiableCollection(addons.values()); | ||
Set<SkriptAddon> addons = new HashSet<>(Skript.addons); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use a name->addon map here rather than a set, instead of whatever you're doing with streams below
public static void register(SyntaxRegistry registry, Class<? extends Condition> condition, PropertyType propertyType, String property, String type) { | ||
if (type.contains("%")) | ||
throw new SkriptAPIException("The type argument must not contain any '%'s"); | ||
SyntaxInfo.Builder<?, ? extends Condition> builder = SyntaxInfo.builder(condition).priority(DEFAULT_PRIORITY); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think there should be another method for letting the user specify their own priority for the element?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now, the methods return the SyntaxInfo that was registered. It would be possible to construct a builder from that SyntaxInfo, change the priority, and then register it again. Of course, having to unregister and then register again is not ideal, but needing to change the priority is not exactly common.
An alternative approach could be to revamp these methods to just return a SyntaxInfo (and then you have to register it yourself), but I'm not sure if that's ideal (and it might conflict with the existing method). What do you think?
src/main/java/org/skriptlang/skript/bukkit/registration/package-info.java
Outdated
Show resolved
Hide resolved
Allows converting SyntaxInfos back into Builders
Fixes current test failures
Description
This is built from the work done in #5331 (thanks @kiip1)
This PR is the initial effort to build a modern API for Skript. All new API has been marked as experimental. Backwards compatibility has been kept. The focus of this PR is to introduce a new addon registration process and syntax registration process. It also implements many new utility interfaces and classes for usage across Skript.
Breaking Changes
New Skript Core
The new Skript interface represents the core of the language. At this time, implementations require methods for registering an addon and obtaining a collection of all addons. The Skript interface is an extension of the SkriptAddon interface, which is further described below.
A default implementation is available, which is what the plugin uses. Below is an example of creating a Skript instance using the default implementation.
New Addon API
The SkriptAddon interface is the center of the new Addon API. At this time, every SkriptAddon has a name, syntax registry, and localizer (the last two are described in further detail below)
An addon can be registered as follows:
Addon Modules
Also included in this new API are AddonModules, which enables Skript and its addons to be broken up into categories. For example, a Skript implementation not dependent on Minecraft might have a default module containing something like arithmetic.
Modules are functional interfaces with one method,
load
. The load method has aSkriptAddon
parameter that can be used for accessing the syntax registry, localizer, and other things when they are added!Loading a module is rather simple:
Note: loadModules() is simply a helper method for `new MathModule().load(myAddon)
New Syntax Registration API
The syntax registration API is the biggest component of this PR. The registration process has been completely overhauled to provide greater control over the registration process.
Syntax Infos
The SyntaxInfo interface is a replacement for the SyntaxElementInfo class.
Every SyntaxInfo has the following properties:
Two default extensions exist:
Additionally, one Bukkit-specific implementation exists for Bukkit events:
Builders
Four builders exist for creating a SyntaxInfo, SyntaxInfo.Expression, SyntaxInfo.Structure, and BukkitInfos.Event. These builders are implemented using the new Builder/Buildable interfaces, described below.
The following is an example of building a SyntaxInfo for LitPi with the builder for SyntaxInfo.Expression:
SyntaxOrigin
A SyntaxOrigin describes where a syntax has come from. By default, it only has a name (String) which describes the origin. When specified, this would likely be the name of an addon (see AddonOrigin). When not specified, it is the fully qualified name of the SyntaxElement class.
Priorities
The priority system is an evolution of ExpressionType. Unlike ExpressionType, which is only for expressions, the priority system applies to all SyntaxInfos. By default, Skript has three priorities:
ExpressionType#EVENT
andExpressionType#PROPERTY
have been replaced with the constantsEventValueExpression#DEFAULT_PRIORITY
andPropertyExpression#DEFAULT_PRIORITY
respectively. The former lies between SIMPLE/COMBINED while the latter lies between COMBINED/PATTERN_MATCHES_EVERYTHINGAdditionally,
PropertyCondition#DEFAULT_PRIORITY
has been added for conditions. It also lies between COMBINED/PATTERN_MATCHES_EVERYTHING.Unlike Structure priorities, this new Priority system is purely relational. There are no "magic" numbers that determine position. The definition for the default three may be useful for visualizing this:
Additionally, a Priority may be created that comes before some other Priority. If one wanted to create a Priority for syntax that should be parsed really early, they might do something like:
SyntaxRegistry
The SyntaxRegistry is the home for an addon's syntax infos. A SyntaxRegistry has three important methods:
syntaxes(Key)
which returns all SyntaxInfos registered under a Key (e.g. all expressions, effects, etc.)register(Key, SyntaxInfo)
which registers a SyntaxInfo under a Keyunregister(Key, SyntaxInfo)
which unregisters a SyntaxInfo that is stored under a KeyA default implementation of a SyntaxRegistry may be obtained through:
Keys
Keys are used for storing SyntaxInfos of a certain type. Skript has six built in keys:
STRUCTURE
for structuresSECTION
for sectionsSTATEMENT
for effects and conditions (Statement classes)EFFECT
for effectsCONDITION
for conditionsEXPRESSION
for expressionsCreating a Key constant is simple:
Now, if we wanted to access the Statements in a registry:
Child Keys
Child Keys are the same as Keys, but they also have a parent Key. Thus, when a SyntaxInfo is registered under a Child Key, it is also registered under the parent Key. This is how the register for Statements works, as the Effect and Condition Keys are Child Keys of the Statement key. We can use this example to see how to build Child Keys:
Experimental Localization API
This PR includes an experimental (and likely subject to change) API for Localization. At this point in time, it exists for modern addons to register their language files. I avoided creating too much API as that will be for a separate PR reworking localization. The key idea here is loading language files, which can be done as follows:
setSourceDirectories
takes in two parameters:languageFileDirectory
which is the path to the language file directory on the jar.dataFileDirectory
(optional) which is the path to the language file directory on the disk (e.g. user customizable lang files).New Utilities
Builder/Buildable Interfaces
I have introduced two new interfaces for building objects along with converting objects into builders. The primary interface, builder, has two methods:
build()
which is the terminal operation for a builder that returns an instance of the type being built.applyTo(Builder)
which enables applying (copying) the values of one builder onto another.Another interface, Buildable, enables converting an object back into a builder. It has one method:
builder()
which returns a builder representing the object.For example, one might want to add a new pattern to a SyntaxInfo. This is now trivial:
ClassLoader API
I have introduced a ClassLoader utility class which acts as a replacement for the
SkriptAddon#loadClasses
method. It takes inspiration from the changes I had made in #4573.A simple utility method functioning the same as
SkriptAddon#loadClasses
exists too:However, a builder exists for creating custom ClassLoaders with different behavior:
It is possible to load classes without passing
getFile()
(or a jar), though it is not preferred due to reliability reasons. It worked fine during my testing, but the documentation for the utility used (ClassPath from Guava) notes some potential issues.ViewProvider Interface
An interface has been added to represent objects that can have unmodifiable views of themselves created. An unmodifiable view allows read access into an object but prevents making any changes to its values. For example, an unmodifiable SkriptAddon would allow you to obtain properties such as its name, but it would prevent changes such as storing a new registry or loading a new module.
Target Minecraft Versions: any
Requirements: none
Related Issues: